import MeshRender2D from "./MeshRender2D";

export default class MeshAssembler extends cc["Assembler"] {
    private _renderData: any;
    private _floatsPerVert = 5;
    private _colorOffset = 4;
    private _uvOffset = 2;

    constructor() {
        super();
        this._renderData = new cc["RenderData"]();
        this._renderData.init(this);
        this._renderData.createFlexData(0, 4, 6, this["getVfmt"]());
        
    }

    updateRenderData(meshRender: MeshRender2D) {
        this.flexBuffCheck(meshRender);
        this.updateUVs(meshRender);
        this.updateIndice(meshRender);
        this.updateVerts(meshRender);
    }

    updateColor(comp: MeshRender2D, color) {
        this.flexBuffCheck(comp);
        let uintVerts = this._renderData.uintVDatas[0];
        if (!uintVerts) return;
        color = color != null ? color : comp.node.color["_val"];
        for (let i = this._colorOffset, l = uintVerts.length; i < l; i += this._floatsPerVert) {
            uintVerts[i] = color;
        }
    }

    private updateUVs(comp: MeshRender2D) {
        let uv = comp.uvs;
        let verts = this._renderData.vDatas[0];
        for (let i = 0, len = uv.length; i < len; i++) {
            let dstOffset = this._floatsPerVert * i + this._uvOffset;
            verts[dstOffset] = uv[i].x;
            verts[dstOffset + 1] = uv[i].y;
        }
    }

    private updateIndice(comp: MeshRender2D) {
        let ids = comp.indices;
        let indices = this._renderData.iDatas[0];
        for (let i = 0, len = ids.length; i < len; i++) {
            indices[i] = ids[i];
        }
    }

    private updateVerts(comp: MeshRender2D) {
        let verts = this._renderData.vDatas[0];

        let matrix = comp.node["_worldMatrix"];
        let matrixm = matrix.m,
            a = matrixm[0], b = matrixm[1], c = matrixm[4], d = matrixm[5],
            tx = matrixm[12], ty = matrixm[13];

        let vertexOffset = 0;
        let justTranslate = a === 1 && b === 0 && c === 0 && d === 1;

        let pos: cc.Vec2;
        if (justTranslate) {
            for (let i = 0, len = comp.vertexs.length; i < len; i++) {
                pos = comp.vertexs[i];
                verts[vertexOffset] = pos.x + tx;
                verts[vertexOffset + 1] = pos.y + ty;
                vertexOffset += this._floatsPerVert;
            }
        } else {
            for (let i = 0, len = comp.vertexs.length; i < len; i++) {
                pos = comp.vertexs[i];
                verts[vertexOffset] = a * pos.x + c * pos.y + tx;
                verts[vertexOffset + 1] = b * pos.x + d * pos.y + ty;
                vertexOffset += this._floatsPerVert;
            }
        }
    }

    fillBuffers(comp: MeshRender2D, renderer) {
        if (renderer.worldMatDirty) {
            this.updateVerts(comp);
        }

        let renderData = this._renderData;
        let vData = renderData.vDatas[0];
        let iData = renderData.iDatas[0];

        let buffer = renderer.getBuffer("mesh", this["getVfmt"]());
        let offsetInfo = buffer.request(comp.vertexs.length, comp.indices.length);

        // buffer data may be realloc, need get reference after request.

        // fill vertices
        let vertexOffset = offsetInfo.byteOffset >> 2,
            vbuf = buffer._vData;

        if (vData.length + vertexOffset > vbuf.length) {
            vbuf.set(vData.subarray(0, vbuf.length - vertexOffset), vertexOffset);
        } else {
            vbuf.set(vData, vertexOffset);
        }

        // fill indices
        let ibuf = buffer._iData,
            indiceOffset = offsetInfo.indiceOffset,
            vertexId = offsetInfo.vertexOffset;
        for (let i = 0, l = iData.length; i < l; i++) {
            ibuf[indiceOffset++] = vertexId + iData[i];
        }
    }

    private flexBuffCheck(comp: MeshRender2D) {
        let { vData, usedVertices, usedIndices, usedVerticesFloats } = this._renderData._flexBuffer;
        if (comp.vertexs.length !== usedVertices || comp.indices.length !== usedIndices) {
            this._renderData._flexBuffer.reserve(comp.vertexs.length, comp.indices.length);
        }
    }
}

cc["Assembler"].register(MeshRender2D, MeshAssembler);